
`include "common_header.verilog"

//  *************************************************************************
//  File : tx_ff_read_cntlv2
//  *************************************************************************
//  This program is controlled by a written license agreement.
//  Unauthorized Reproduction or Use is Expressly Prohibited.
//  Copyright (c) 2002 MoreThanIP.com, Germany
//  Designed by : Francois Balay
//  fbalay@morethanip.com
//  *************************************************************************
//  Decription : Transmit FIFO Frame Length Calculation
//  Version    : $Id: tx_ff_read_cntlv2.v,v 1.5 2017/05/03 12:18:59 dk Exp $
//  *************************************************************************

module tx_ff_read_cntlv2 (
   clk,
   clk_ena,
   reset,
   sw_reset_rclk,
   ff_rden,
   ff_sav,
   tx_aempty,
   mac_rden,
   stat_rden_reg,
   stat_empty,
   stat_aempty,
   frm_length);

input   clk;                    //  MAC Transmit Clock
input   clk_ena;                //  MAC Transmit Clock Enable
input   reset;                  //  Global Reset
input   sw_reset_rclk;          //  sync reset on read clock domain
output  ff_rden;                //  Data FIFO Read Enable
input   ff_sav;                 //  Section Available
input   tx_aempty;              //  FIFO Almost Empty
input   mac_rden;               //  MAC Read Enable
output  stat_rden_reg;          //  Read Status FIFO at end of frame
input   stat_empty;             //  Status FIFO Empty
input   stat_aempty;            //  Status FIFO Almost Empty
input   [12:0] frm_length;      //  Frame Length

wire    ff_rden;
wire    stat_rden_reg;

// TYPE stm_typ:
parameter STM_TYP_IDLE          = 3'd0;
parameter STM_TYP_LOAD_PIPE     = 3'd1;
parameter STM_TYP_WAIT_READ     = 3'd2;
parameter STM_TYP_FF_READ       = 3'd3;
parameter STM_TYP_END_FRM1      = 3'd4;

reg     [2:0] state;
reg     [2:0] nextstate;
reg     [12:0] frm_cnt;
reg     frm_cnt_dec;
reg     [12:0] frm_length_reg;
reg     tx_aempty_d;
wire    tx_aempty_reg;
wire    stat_empty_fin;         // true data valid depending on memory read support with extra delay

`ifdef MTIPSDPM_GEN_READEN
reg     stat_empty_d;

// when memory will act with read enable it will not produce data before empty deasserts.
// hence memory output will still present the old output during first cycle.

always @(posedge reset or posedge clk)
   begin : process_1a
   if (reset == 1'b 1)
      begin
      stat_empty_d <= 1'b 1;
      end
   else
      begin
      stat_empty_d <= stat_empty;
      end
   end

assign stat_empty_fin = stat_empty | stat_empty_d;      // deassert delayed, assert immediately

`else

assign stat_empty_fin = stat_empty;

`endif

//  Input Register
//  --------------
always @(posedge reset or posedge clk)
   begin : process_1
   if (reset == 1'b 1)
      begin
      frm_length_reg <= {13{1'b 0}};
      tx_aempty_d <= 1'b 1;
      end
   else
      begin
      if (clk_ena == 1'b 1)
         begin
         if (stat_empty_fin == 1'b 0)
            begin
            frm_length_reg <= frm_length;
            end
         else
            begin
            frm_length_reg <= {13{1'b 0}};
            end

         if (stat_empty == 1'b 1 & tx_aempty == 1'b 1)
            begin
            tx_aempty_d <= 1'b 1;
            end
         else
            begin
            tx_aempty_d <= 1'b 0;
            end
         end
      end
   end

assign tx_aempty_reg = (stat_empty_fin & tx_aempty) | tx_aempty_d;

//  Length Counter
//  --------------
always @(posedge reset or posedge clk)
   begin : process_2
   if (reset == 1'b 1)
      begin
      frm_cnt <= {13{1'b 0}};
      end
   else
      begin
      if (clk_ena == 1'b 1)
         begin
         if (state == STM_TYP_FF_READ)
            begin
            frm_cnt <= frm_cnt + 13'd 1;   //  relevant for all frames > 2 words
            end
         else if (state != STM_TYP_WAIT_READ )
            begin
            frm_cnt <= 13'd 2;             //  Note: this only works with "2". Otherwise frm_length_reg=2 comparison at frm_cnt_dec is necessary!
            end
         end
      end
   end

always @(posedge reset or posedge clk)
   begin : process_3
   if (reset == 1'b 1)
      begin
      frm_cnt_dec <= 1'b 0;
      end
   else
      begin
      if (clk_ena == 1'b 1)
         begin
         if (state == STM_TYP_WAIT_READ & frm_cnt_dec == 1'b 1 |
             state == STM_TYP_FF_READ & frm_length_reg == frm_cnt)
            begin
            frm_cnt_dec <= 1'b 1;
            end
         else
            begin
            frm_cnt_dec <= 1'b 0;
            end
         end
      end
   end

//  Control State Machine
//  ---------------------
always @(posedge reset or posedge clk)
   begin : process_4
   if (reset == 1'b 1)
      begin
      state <= STM_TYP_IDLE;
      end
   else
      begin
      if (sw_reset_rclk == 1'b 1)
         begin
         state <= STM_TYP_IDLE;
         end
      else if (clk_ena == 1'b 1)
         begin
         state <= nextstate;
         end
      end
   end

always @(state or ff_sav or stat_empty or mac_rden or frm_cnt_dec or
         stat_aempty or frm_length_reg or tx_aempty_reg)
   begin : process_5
   case (state)
   STM_TYP_IDLE:
      begin
      if (ff_sav == 1'b 1 | stat_empty == 1'b 0)
         begin
         //  Preload FIFO Read Pipeline
         //  --------------------------
         nextstate = STM_TYP_LOAD_PIPE;
         end
      else
         begin
         nextstate = STM_TYP_IDLE;
         end
      end

   STM_TYP_LOAD_PIPE:
      begin
      if (mac_rden == 1'b 1)
         begin
         nextstate = STM_TYP_FF_READ;
         end
      else
         begin
         nextstate = STM_TYP_WAIT_READ;
         end
      end

   STM_TYP_WAIT_READ:
      begin
      if (mac_rden == 1'b 1 & tx_aempty_reg == 1'b 0)
         begin
         nextstate = STM_TYP_FF_READ;
         end
      else
         begin
         nextstate = STM_TYP_WAIT_READ;
         end
      end

   STM_TYP_FF_READ:
      begin
      //  Minimum Frame Length
      //  --------------------
      if (frm_length_reg == 13'd 1)
         begin
         nextstate = STM_TYP_END_FRM1;
         end
      else if (frm_cnt_dec == 1'b 1 )
         begin
         nextstate = STM_TYP_END_FRM1;
         end
      else if (tx_aempty_reg == 1'b 1 )
         begin
         nextstate = STM_TYP_WAIT_READ;   //  protect against underflow!
         end
      else
         begin
         nextstate = STM_TYP_FF_READ;
         end
      end

   STM_TYP_END_FRM1:
      begin
      //  Subsequent Frame Available, consider status FIFO latency
      //  --------------------------
      if (stat_aempty == 1'b 0 | ff_sav == 1'b 1)
         begin
         nextstate = STM_TYP_LOAD_PIPE;
         end
      else
         begin
         nextstate = STM_TYP_IDLE;
         end
      end

   default:
      begin
      nextstate = STM_TYP_IDLE;
      end

   endcase
   end

//  FIFO Read
//  ---------

assign ff_rden = nextstate == STM_TYP_FF_READ | nextstate == STM_TYP_LOAD_PIPE ? 1'b 1 : 1'b 0;

//  Status FIFO Read immediately with EOP
//  --------------------------------------
// Must assert immediately as with shortest STM transition END_FRM1->LOAD_PIPE->FF_READ the 
// frm_length_reg must be valid during FF_READ's first cycle. 

assign stat_rden_reg = (clk_ena == 1'b 1 && nextstate == STM_TYP_END_FRM1) ? 1'b 1 : 1'b 0;


endmodule // module tx_ff_read_cntlv2
